﻿#region << 文件说明 >>

/*----------------------------------------------------------------
 * 命名空间：Vampirewal.Core.WPF.Theme.CustomControl.PropertyInfoPanel
 * 唯一标识：38f7614b-ec6d-4323-a6cc-c80726962ff2
 * 文件名：PropertyInfoPanel
 *
 * 创建者：杨程
 * 电子邮箱：235160615@qq.com
 * 创建时间：2024/6/3 16:45:12
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

#endregion << 文件说明 >>

using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace Vampirewal.Core.WPF.Theme;

/// <summary>
/// 属性面板控件
/// </summary>
public partial class PropertyInfoPanel : Control
{
    /// <summary>
    /// 构造函数
    /// </summary>
    public PropertyInfoPanel()
    {
        var BaseStyle = res["PropertyInfoPanelStyle"] as Style;

        this.Style = BaseStyle;
    }

    private ResourceDictionary res
    {
        get
        {
            return new ResourceDictionary() { Source = new Uri("pack://application:,,,/Vampirewal.Core.WPF.Theme;component/Theme.xaml", UriKind.RelativeOrAbsolute) };
        }
    }

    private StackPanel ControlParent { get; set; }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        ControlParent = this.Template.FindName("controlParent", this) as StackPanel;
    }

    #region 依赖属性

    #region 数据源



    /// <summary>
    /// 属性面板数据源
    /// </summary>
    public object PanelSource
    {
        get { return (object)GetValue(PanelSourceProperty); }
        set { SetValue(PanelSourceProperty, value); }
    }

    // Using a DependencyProperty as the backing store for PropertyInfoPanelSource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PanelSourceProperty =
        DependencyProperty.Register("PanelSource", typeof(object), typeof(PropertyInfoPanel), new PropertyMetadata(null, PanelSourceCallback));

    private static void PanelSourceCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var panel = d as PropertyInfoPanel;

        panel.ControlParent.SetValue(StackPanel.WidthProperty, panel.Width - 20);

        if (e.NewValue == null)
            return;
        else
            panel.ControlParent.Children.Clear();

        //获取到数据上下文
        var dc = panel.DataContext;
        //获取数据上下文的Type
        var dcType = dc.GetType();
        //获取这个DataContext中标记了属性面板特性的属性
        var Info = dcType.GetProperties()
            .Where(w=> w.GetCustomAttribute<PropertyInfoPanelAttribute>()!=null)
            .FirstOrDefault(w => 
            {
                var va= w.GetValue(dc);

                if (va == null)
                    return false;

                if (!va.Equals(panel.PanelSource))
                    return false;

                if(!w.PropertyType.IsAssignableFrom(panel.PanelSource.GetType()))
                    return false;

                return true;

                //return propAttr != null && w.PropertyType.IsAssignableFrom(panel.PanelSource.GetType());
            });

        if (Info == null)
            return;

        //获取这个属性上的面板特性
        var panelAttr = Info.GetCustomAttribute<PropertyInfoPanelAttribute>();

        var props = e.NewValue.GetType().GetProperties();

        List<GroupBox> groups = new List<GroupBox>();

        #region textbox的错误验证显示模版

        ControlTemplate ErrorCT = new ControlTemplate();

        var sp = new FrameworkElementFactory(typeof(StackPanel));
        //sp.SetValue(StackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Stretch);
        //sp.SetValue(StackPanel.WidthProperty, panel.Width-50);

        var ado = new FrameworkElementFactory(typeof(AdornedElementPlaceholder)) { Name = "ado" };
        sp.AppendChild(ado);

        var tblock = new FrameworkElementFactory(typeof(TextBlock));
        tblock.SetValue(TextBlock.WidthProperty, new TemplateBindingExtension(AdornedElementPlaceholder.ActualWidthProperty));
        tblock.SetValue(TextBlock.MarginProperty, new Thickness(0, 5, 0, 0));
        tblock.SetValue(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Left);
        tblock.SetValue(TextBlock.VerticalAlignmentProperty, VerticalAlignment.Top);
        tblock.SetValue(TextBlock.FontFamilyProperty, new FontFamily("宋体"));
        tblock.SetValue(TextBlock.FontSizeProperty, 12.0);
        tblock.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold);
        tblock.SetValue(TextBlock.ForegroundProperty, Brushes.Red);
        tblock.SetValue(TextBlock.TextProperty, new Binding("AdornedElement.(Validation.Errors)[0].ErrorContent") { ElementName = "ado" });
        tblock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.WrapWithOverflow);
        sp.AppendChild(tblock);

        ErrorCT.VisualTree = sp;
        #endregion

        #region textbox的错误验证触发器

        var textBoxStyle = new Style(typeof(TextBox));

        if (!string.IsNullOrEmpty(panel.TextBoxStyleName))
        {
            Style baseStyle = (Style)System.Windows.Application.Current.Resources[panel.TextBoxStyleName];

            textBoxStyle.BasedOn = baseStyle;
        }
        textBoxStyle.Setters.Add(new Setter(Validation.ErrorTemplateProperty, ErrorCT));

        Trigger trigger = new Trigger()
        {
            Property = Validation.HasErrorProperty,
            Value = true,
        };
        trigger.Setters.Add(new Setter(TextBox.ToolTipProperty, new Binding("(Validation.Errors)[0].ErrorContent")
        {
            RelativeSource = new RelativeSource(RelativeSourceMode.Self)
        }));

        
        textBoxStyle.Triggers.Add(trigger);

        #endregion



        foreach (var prop in props)
        {
            if (prop.GetCustomAttribute<TextPropertyInfoAttribute>() is TextPropertyInfoAttribute text)
            {
                panel.CreatePropertyInfoItem( text, panelAttr.Columns, () =>
                {
                    TextBox textbox = new TextBox()
                    {
                        IsReadOnly = text.IsReadOnly,
                        //Width = 120d
                        Margin = new Thickness(0, 2, 0, 2)
                    };

                    string bindPath = $"{Info.Name}.{prop.Name}";

                    Binding bind = new Binding(bindPath);
                    bind.Mode = BindingMode.TwoWay;
                    bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

                    var validationList = prop.GetCustomAttributes().OfType<System.ComponentModel.DataAnnotations.ValidationAttribute>().ToList();

                    bind.ValidationRules.Add(new VampirewalValidationRule() { Validations = validationList });

                    textbox.SetBinding(TextBox.TextProperty, bind);
                    //textbox.SetValue(Validation.ErrorTemplateProperty, ErrorCT);
                    // 应用样式
                    textbox.Style = textBoxStyle;

                    

                    return textbox;
                }, ref groups);
            }
            else if (prop.GetCustomAttribute<NumberTextPropertyInfoAttribute>() is NumberTextPropertyInfoAttribute numberText)
            {
                panel.CreatePropertyInfoItem( numberText, panelAttr.Columns, () =>
                {
                    TextBox textbox = new TextBox()
                    {
                        IsReadOnly = numberText.IsReadOnly,
                        //Width = 120d
                        Margin=new Thickness(0,2,0,2)
                    };

                    string bindPath = $"{Info.Name}.{prop.Name}";

                    Binding bind = new Binding(bindPath);
                    bind.Mode = BindingMode.TwoWay;
                    bind.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;

                    var validationList = prop.GetCustomAttributes().OfType<System.ComponentModel.DataAnnotations.ValidationAttribute>().ToList();

                    bind.ValidationRules.Add(new VampirewalValidationRule() { Validations = validationList });

                    textbox.SetBinding(TextBox.TextProperty, bind);
                    //textbox.SetValue(Validation.ErrorTemplateProperty, ErrorCT);
                    // 应用样式
                    textbox.Style = textBoxStyle;

                    

                    return textbox;
                }, ref groups);
            }
        }

        groups=groups.OrderByDescending(o => Convert.ToInt32(o.Tag)).ToList();

        groups.ForEach(f =>
        {
            f.Margin = new Thickness(panelAttr.MarginLeft, panelAttr.MarginTop, panelAttr.MarginRight, panelAttr.MarginBottom);

            panel.SortUniformGridChildren(f);

            panel.ControlParent.Children.Add(f);
        });
    }

    private void SortUniformGridChildren(GroupBox group)
    {
        UniformGrid uniformGrid = group.Content as UniformGrid;
        if (uniformGrid == null) return;

        List<UIElement> childrenList = new List<UIElement>();

        foreach (UIElement child in uniformGrid.Children)
        {
            childrenList.Add(child);
        }

        uniformGrid.Children.Clear();

        childrenList.Sort((a, b) =>
        {
            if (a is TitleContent titleContentA && b is TitleContent titleContentB)
            {
                if( Convert.ToInt32(titleContentA.Tag) > Convert.ToInt32(titleContentB.Tag))
                {
                    return 1;
                }
                else
                {
                    return 0;
                }
            }
            return 0;
        });

        foreach (UIElement child in childrenList)
        {
            uniformGrid.Children.Add(child);
        }
    }

    private void CreatePropertyInfoItem( PropertyInfoAttribute propertyInfo, int columns, Func<TextBox> textbox, ref List<GroupBox> groups)
    {
        var group = groups.FirstOrDefault(f => f.Header.ToString() == propertyInfo.GroupName);
        if (group == null)
        {
            group = new GroupBox()
            {
                Header = propertyInfo.GroupName,
            };

            group.Content = new UniformGrid()
            {
                Columns = columns,
                HorizontalAlignment = HorizontalAlignment.Stretch,
            };
            group.Tag = groups.Count;
            group.HorizontalAlignment = HorizontalAlignment.Stretch;

            groups.Add(group);

            
        }

        TitleContent title = new TitleContent()
        {
            Title = propertyInfo.Title,
            TitleWidth = propertyInfo.TitleWidth,
            TitleVerticalAlignment = VerticalAlignment.Center,
            TitleForeground = Brushes.Black,
            Tag= propertyInfo.SortIndex,
            HorizontalContentAlignment = HorizontalAlignment.Stretch,
        };

        var tb = textbox.Invoke();

        title.Content = tb;

        
        (group.Content as UniformGrid).Children.Add(title);

    }

    

    #endregion

    #region 界面渲染

    #region 背景Border圆角

    /// <summary>
    /// 背景Border圆角
    /// </summary>
    public CornerRadius BackgroundCornerRadius
    {
        get { return (CornerRadius)GetValue(BackgroundCornerRadiusProperty); }
        set { SetValue(BackgroundCornerRadiusProperty, value); }
    }

    // Using a DependencyProperty as the backing store for BackgroundCornerRadius.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BackgroundCornerRadiusProperty =
        DependencyProperty.Register("BackgroundCornerRadius", typeof(CornerRadius), typeof(PropertyInfoPanel), new PropertyMetadata(new CornerRadius(5)));

    #endregion

    #endregion

    #region 文本框继承的样式

    /// <summary>
    /// 文本框样式
    /// </summary>
    public Style TextBoxStyle
    {
        get { return (Style)GetValue(TextBoxStyleProperty); }
        set { SetValue(TextBoxStyleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for TextBoxStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TextBoxStyleProperty =
        DependencyProperty.Register("TextBoxStyle", typeof(Style), typeof(PropertyInfoPanel), new PropertyMetadata(null));

    public string TextBoxStyleName
    {
        get { return (string)GetValue(TextBoxStyleNameProperty); }
        set { SetValue(TextBoxStyleNameProperty, value); }
    }

    // Using a DependencyProperty as the backing store for TextBoxStyleName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TextBoxStyleNameProperty =
        DependencyProperty.Register("TextBoxStyleName", typeof(string), typeof(PropertyInfoPanel), new PropertyMetadata(""));

    #endregion

    #endregion
}

public class VampirewalValidationRule : ValidationRule
{
    public VampirewalValidationRule()
    {
        Validations = new List<System.ComponentModel.DataAnnotations.ValidationAttribute>();
    }

    public List<System.ComponentModel.DataAnnotations.ValidationAttribute> Validations { get; set; }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (Validations.Count > 0)
        {
            bool result = true;
            string error = string.Empty;

            foreach (var item in Validations)
            {
                result = item.IsValid(value);
                if (!result)
                    error += item.ErrorMessage;
            }

            if (!result)
                return new ValidationResult(result, error);
        }

        return ValidationResult.ValidResult;
    }
}